package org.activequant.calendar;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import org.activequant.core.types.TimeStamp;
import org.activequant.core.util.Check;
import org.activequant.util.calendar.CalendarUtils;

/**
 * A time-bound calendar entry is a calendar entry with a specific start and end time.<br>
 * TimeBoundCalendarEntry implements ICalendarEntry. Holds the following associated variables:
 * <ul>
 * <li>entryStartAsMillis(long)</li>
 * <li>entryEndAsMillis(long)</li>
 * </ul>
 * <br>
 * <b>History:</b><br>
 * - [2 aug 2008] Created (mark)<br>
 * 
 * @author mark
 */
public class TimeBoundCalendarEntry implements ICalendarEntry {
	/**
	 * private final long entryStartAsMillis;<br/>
	 * the start time of the calendar entry in milliseconds since the Epoch GMT time
	 */
	private final long entryStartAsMillis;
	/**
	 * private final long entryEndAsMillis;<br/>
	 * the end time of the calendar entry in milliseconds since the Epoch GMT time
	 */
	private final long entryEndAsMillis;

	/**
	 * Create a new time bound calendar entry given a single date-time for start and end of the entry.<br/>
	 * constructs a TimeBoundCalendarEntry (implements ICalendarEntry) using the given entryStartAndEndAsMillis(long) to set both its associated
	 * entryStartAsMillis(long) and entryEndAsMillis(long).
	 * @param entryStartAndEndAsMillis
	 *            the start and end of the entry in milliseconds since Epoch (GMT time).
	 */
	public TimeBoundCalendarEntry(final long entryStartAndEndAsMillis) {
		this(entryStartAndEndAsMillis, entryStartAndEndAsMillis);
	}

	/**
	 * Create a new time bound calendar entry given a start and end date-time.<br/>
	 * constructs a TimeBoundCalendarEntry (implements ICalendarEntry) using the given entryStartAsMillis(long) and entryEndAsMillis(long) to set its associated
	 * entryStartAsMillis(long) and entryEndAsMillis(long).<br/>
	 * The given entryEndAsMillis(long) can not be less than the entryStartAsMillis(long).
	 * @param entryStartAsMillis
	 *            the start of the entry in milliseconds since Epoch (GMT time).
	 * @param entryEndAsMillis
	 *            the end of the entry in milliseconds since Epoch (GMT time).
	 */
	public TimeBoundCalendarEntry(final long entryStartAsMillis, final long entryEndAsMillis) {
		Check.atLeast("entryEndAsMillis", entryStartAsMillis, entryEndAsMillis);

		this.entryStartAsMillis = entryStartAsMillis;
		this.entryEndAsMillis = entryEndAsMillis;
	}
	/**
	 * returns the associated entryStartAsMillis(long)
	 */
	public long getStartOfEntryAsMillis() {
		return this.entryStartAsMillis;
	}
	/**
	 * returns the associated entryEndAsMillis(long)
	 */
	public long getEndOfEntryAsMillis() {
		return this.entryEndAsMillis;
	}
	/**
	 * returns a String representation of this TimeBoundCalendarEntry (implements ICalendarEntry) in the form:<br/>
	 * "${this.class.name}: start: ${entryStartCal} end: ${entryEndCal}"<br/>
	 * , where entryStartCal and entryEndCal are GMT Calendar representations of the associated entryStartAsMillis(long) and entryEndAsMillis(long)
	 * each formatted as: "${YEAR}-${MONTH}-${DAY_OF_MONTH}T${HOUR_OF_DAY}:${MINUTE}:${SECOND} ${MILLISECOND} ${TimeZone}"
	 */
	@Override
	public String toString() {
		final Calendar start = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
		final Calendar end = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));

		start.setTimeInMillis(this.entryStartAsMillis);
		end.setTimeInMillis(this.entryEndAsMillis);

		return this.getClass().getName() + ": start: " + CalendarUtils.calendarToString(start) + " end: " + CalendarUtils.calendarToString(end);
	}

	/**
	 * returns whether the given now(TimeStamp) is within the interval defined by the associated entryStartAsMillis(long) and entryEndAsMillis(long).<br/>
	 * Note: this implementation will disregard the nanosecond part of now before determining if now is during the
	 * entry.
	 * 
	 * @see org.activequant.calendar.ICalendarEntry
	 */
	public boolean isDuring(final TimeStamp now) {
		final Calendar start = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
		final Calendar end = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));

		start.setTimeInMillis(this.entryStartAsMillis);
		end.setTimeInMillis(this.entryEndAsMillis);

		final Calendar nowAsCalendar = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
		nowAsCalendar.setTimeInMillis(now.getNanoseconds() / CalendarUtils.NANOS_TO_MILLIS);

		return !(nowAsCalendar.before(start) || nowAsCalendar.after(end));
	}

}
